﻿#pragma once

#include "../util/time_util.hh"
#include <cstdint>
#include <thread>

namespace kratos {
namespace util {

/**
 * 帧延时器
 */
class FrameDelayer {
public:
  /**
   * 构造函数
   * @param frame 帧率(帧/秒), 默认为100帧/秒
   */
  FrameDelayer(int frame) : feedback_(0), begin_(0), end_(0), overwork_(0) {
    if (frame > 1000) {
      frame = 1000; // 最大帧率
    }
    // 计算一帧所需的毫秒数
    if (!frame) {
      frame_in_millions = MILLION_RESOLUTION / DEFAULT_FRAME;
    } else {
      frame_in_millions = MILLION_RESOLUTION / frame;
    }
  }

  /**
   * 析构
   */
  ~FrameDelayer() {
    frame_in_millions = 0;
    feedback_ = 0;
    begin_ = 0;
    end_ = 0;
    overwork_ = 0;
  }

  /**
   * 开始帧计时
   */
  inline std::time_t begin_frame() {
    begin_ = kratos::util::get_os_time_millionsecond();
    return (std::time_t)begin_;
  }

  /**
   * 结束计时并延时
   */
  inline std::time_t end_frame() {
    end_ = kratos::util::get_os_time_millionsecond();
    feedback_ = end_ - begin_;
    delay_frame();
    return (std::time_t)end_;
  }

  /**
   * 重置帧率 
   * @param frame 帧率(帧/秒)
   */
  inline void reset_frame(int frame) {
    frame_in_millions = MILLION_RESOLUTION / frame;
  }

private:
  /**
   * 根据帧运行消耗的时间延时
   */
  inline void delay_frame() {
    if (overwork_ >= OVERWORK_THRESHOLD) {
      // 表明系统已经超载，但是不能让逻辑线程完全占满
      std::this_thread::sleep_for(std::chrono::milliseconds(
          0)); // 尝试让出CPU,如果没有>=优先级的就绪线程
      // 可能会继续执行剩余时间片
      return;
    }

    if (feedback_ >= frame_in_millions) {
      // 上次执行的时间表明已经超载,记录超载次数,继续执行
      overwork_ += 1;
      return;
    } else {
      // 未使用完的帧时间 >= 0ms
      uint64_t delta = frame_in_millions - feedback_;

      // 未超载,减少超载次数
      if (overwork_ > 0) {
        overwork_ -= 1;
      }

      // 最少睡眠一个线程时间片
      if (delta < CPU_TIME_SLICE) {
        delta = 1; // 非零值即可最少睡眠一个时间片
      }
      // 不能超过睡眠上限
      if (delta > DELTA_MAX) {
        delta = DELTA_MAX;
      }
      std::this_thread::sleep_for(
          std::chrono::milliseconds((int)delta)); // 睡眠上轮节省的帧时间
    }
  }

private:
  uint64_t frame_in_millions; // 每帧消耗(ms)
  uint64_t feedback_;         // 上一帧消耗(ms)
  uint64_t begin_;            // 帧开始的时间戳(ms)
  uint64_t end_;              // 帧结束的时间戳(ms)
  uint64_t overwork_;         // 超载次数

private:
  static const uint32_t CPU_TIME_SLICE =
      10; // CPU时间片,根据不同系统应该有不同的值,通常为10ms~20ms
  static const uint32_t OVERWORK_THRESHOLD = 5;   // 超载阈值
  static const uint32_t DEFAULT_FRAME = 1000;     // 帧速率
  static const uint32_t MILLION_RESOLUTION = 100; // 1000ms = 1s
  static const uint32_t DELTA_MAX = 100;          // 帧睡眠上限100ms
};

} // namespace util
} // namespace kratos
